Imports System.IO
Imports System.ComponentModel

<Serializable()> _
Public Class Profile

    Public Event CopyStarting As System.ComponentModel.CancelEventHandler
    Public Event CopyStarted As EventHandler
    Public Event CopyProgress As EventHandler(Of CopyProgressEventArgs)
    Public Event CopyComplete As EventHandler
    Public Event CopyCancelled As EventHandler
    Public Event CopyError As EventHandler(Of CopyErrorEventArgs)

    <NonSerialized()> _
    Private _asyncErrorOperation As AsyncOperation

    Private _isCancelPending As Boolean
    Private _isCopying As Boolean

    Public Sub New(ByVal name As String)
        Me._name = name
    End Sub

#Region " Properties "
    Private _name As String
    Public Property Name() As String
        Get
            Return Me._name
        End Get
        Set(ByVal value As String)
            If Me._name <> value Then
                Me._name = value
                Me.IsDirty = True
            End If
        End Set
    End Property

    Private _targetFolders As New FileSystemInfoCollection(Of System.IO.DirectoryInfo)(Me)
    Public ReadOnly Property TargetFolders() As System.Collections.Generic.ICollection(Of System.IO.DirectoryInfo)
        Get
            Return Me._targetFolders
        End Get
    End Property

    Private _sourceFiles As New FileSystemInfoCollection(Of System.IO.FileInfo)(Me)
    Public ReadOnly Property SourceFiles() As ICollection(Of System.IO.FileInfo)
        Get
            Return Me._sourceFiles
        End Get
    End Property

    <NonSerialized()> _
    Private _isDirty As Boolean
    Public Property IsDirty() As Boolean
        Get
            Return Me._isDirty
        End Get
        Friend Set(ByVal value As Boolean)
            Me._isDirty = value
        End Set
    End Property
#End Region

#Region " Public Methods "
    Public Sub CopySourceFilesToTargetFolders()
        Dim cancelArgs As New CancelEventArgs()
        Me.OnCopyStarting(cancelArgs)

        If cancelArgs.Cancel Then
            Exit Sub
        End If

        Me._asyncErrorOperation = AsyncOperationManager.CreateOperation(Nothing)
        System.Threading.ThreadPool.QueueUserWorkItem(AddressOf Me.CopyInternal)

        Me.OnCopyStarted(EventArgs.Empty)
    End Sub

    Public Sub CancelCopy()
        If Me._isCopying Then
            Me._isCancelPending = True
        End If
    End Sub

    Public Sub AddFolder(ByVal path As String)
        Me._targetFolders.Add(New DirectoryInfo(path))
        System.Diagnostics.Trace.WriteLine("Folder Added: " + path)
    End Sub

    Public Sub RemoveFolder(ByVal path As String)
        Me._targetFolders.Remove(path)
    End Sub

    Public Sub AddFile(ByVal fileName As String)
        Me._sourceFiles.Add(New FileInfo(fileName))
        System.Diagnostics.Trace.WriteLine("File Added: " + fileName)
    End Sub

    Public Sub RemoveFile(ByVal fileName As String)
        Me._sourceFiles.Remove(fileName)
    End Sub
#End Region

#Region " Private Methods "
    Private Sub CopyInternal(ByVal state As Object)
        Me._asyncErrorOperation.Post(AddressOf Me.PostCopyStarted, Nothing)

        Me._isCopying = True

        Dim totalCopies As Integer = Me._targetFolders.Count * Me._sourceFiles.Count
        Dim currentCopy As Integer = 0

        For Each di As DirectoryInfo In Me._targetFolders
            For Each fi As FileInfo In Me._sourceFiles
                'check for cancellation before continuing the loop
                If Me._isCancelPending Then
                    Me._isCancelPending = False
                    Me._asyncErrorOperation.Post(AddressOf Me.OnCopyCancelled, EventArgs.Empty)
                    Exit Sub
                End If

                'send progress report back to main thread (made easier by the BackgroundWorker class)
                currentCopy += 1
                Dim percent As Integer = CInt(Math.Round((currentCopy / totalCopies) * 100))
                Dim destFile As New FileInfo(Path.Combine(di.FullName, fi.Name))
                Me._asyncErrorOperation.Post(AddressOf Me.PostCopyProgress, New CopyProgressEventArgs(destFile, percent))

                'copy file, trap IOExceptions and raise CopyError event on main thread
                Try
                    fi.CopyTo(destFile.FullName)
                Catch ex As System.IO.IOException
                    Me._asyncErrorOperation.Post(AddressOf Me.OnCopyError, New CopyErrorEventArgs(destFile, ex))
                End Try
            Next
        Next

        Me._isCopying = False

        Me._asyncErrorOperation.PostOperationCompleted(AddressOf Me.PostCopyCompleted, EventArgs.Empty)
    End Sub

    Private Sub PostCopyCompleted(ByVal state As Object)
        Me.OnCopyCompleted(Me, CType(state, EventArgs))
    End Sub

    Private Sub PostCopyProgress(ByVal state As Object)
        Me.OnCopyProgress(Me, CType(state, CopyProgressEventArgs))
    End Sub

    Private Sub PostCopyStarted(ByVal state As Object)
        Me.OnCopyStarted(EventArgs.Empty)
    End Sub

    Private Sub OnCopyStarted(ByVal e As EventArgs)
        RaiseEvent CopyStarted(Me, e)
    End Sub

    Private Sub OnCopyStarting(ByVal e As CancelEventArgs)
        RaiseEvent CopyStarting(Me, e)
    End Sub

    Private Sub OnCopyCancelled(ByVal state As Object)
        RaiseEvent CopyCancelled(Me, DirectCast(state, EventArgs))
    End Sub

    Private Sub OnCopyError(ByVal state As Object)
        RaiseEvent CopyError(Me, DirectCast(state, CopyErrorEventArgs))
    End Sub

    Private Sub OnCopyProgress(ByVal sender As Object, ByVal e As CopyProgressEventArgs)
        RaiseEvent CopyProgress(Me, e)
    End Sub

    Private Sub OnCopyCompleted(ByVal sender As Object, ByVal e As EventArgs)
        RaiseEvent CopyComplete(Me, EventArgs.Empty)
    End Sub
#End Region
End Class
